import os
import json
from tkinter import filedialog, messagebox
from PIL import Image, ImageDraw, ImageFont
from config import APP_TITLE, SUPPORTED_IMAGE_FORMATS


class FileOperations:
    """文件操作相关功能"""
    
    def __init__(self, app):
        self.app = app

    def open_image(self):
        """打开图片文件"""
        path = filedialog.askopenfilename(filetypes=SUPPORTED_IMAGE_FORMATS)
        if not path:
            return
        self.app.image_path = path
        self.app.image = Image.open(path).convert("RGBA")
        self.app.fit_to_window()

    def save_png(self):
        """保存为PNG图片"""
        if not self.app.image:
            return
            
        # 生成默认文件名
        try:
            default_name = self._generate_default_filename("png")
        except:
            default_name = "已标注.png"
        
        try:
            save_path = filedialog.asksaveasfilename(
                defaultextension=".png", 
                filetypes=[("PNG files", "*.png"), ("All files", "*.*")],
                initialfile=default_name,
                title="保存PNG图片"
            )
        except Exception as e:
            print(f"保存对话框错误: {e}")
            save_path = filedialog.asksaveasfilename(
                defaultextension=".png", 
                filetypes=[("PNG files", "*.png"), ("All files", "*.*")]
            )
        if not save_path:
            return
            
        # 创建高质量导出图像（2倍超采样）
        original_size = self.app.image.size
        scale_factor = 2  # 2倍超采样，平衡质量和性能
        
        # 创建高分辨率版本
        high_res_size = (original_size[0] * scale_factor, original_size[1] * scale_factor)
        high_res_base = self.app.image.resize(high_res_size, Image.LANCZOS)
        draw = ImageDraw.Draw(high_res_base)
        
        # 绘制高质量线条
        for ln in self.app.lines:
            if not ln.points or len(ln.points) < 2:
                continue
                
            if ln.line_type == "straight":
                # 直线：连接第一个和最后一个点
                start = ln.points[0]
                end = ln.points[-1]
                self._draw_high_quality_line(draw, start[0] * scale_factor, start[1] * scale_factor, 
                                           end[0] * scale_factor, end[1] * scale_factor, 
                                           ln.color, ln.width * scale_factor)
            elif ln.line_type == "polyline":
                # 折线：依次连接所有点
                for i in range(len(ln.points) - 1):
                    start = ln.points[i]
                    end = ln.points[i + 1]
                    self._draw_high_quality_line(draw, start[0] * scale_factor, start[1] * scale_factor,
                                               end[0] * scale_factor, end[1] * scale_factor,
                                               ln.color, ln.width * scale_factor)
        
        # 缩回原始尺寸以获得抗锯齿效果
        base = high_res_base.resize(original_size, Image.LANCZOS)
        
        # 重新创建draw对象用于遮罩和文字绘制（现在base是原始尺寸）
        draw = ImageDraw.Draw(base)
        
        # 绘制遮罩层（按层级顺序）
        for mask in self.app.masks:
            # 计算遮罩颜色（带透明度）
            mask_color = self._get_mask_export_color(mask.color, mask.opacity)
            draw.rectangle(
                [mask.x, mask.y, mask.x + mask.width, mask.y + mask.height],
                fill=mask_color
            )
        
        # 绘制文字标注
        for lbl in self.app.labels:
            font_path = lbl.font_path or self.app.current_font_path
            try:
                pil_font = ImageFont.truetype(font_path, lbl.font_size)
            except Exception:
                pil_font = ImageFont.load_default()
            
            if lbl.bold:
                # 模拟加粗效果
                for dx, dy in [(0, 0), (1, 0), (0, 1), (1, 1)]:
                    draw.text((lbl.x + dx, lbl.y + dy), lbl.text, font=pil_font, fill=lbl.color)
            else:
                draw.text((lbl.x, lbl.y), lbl.text, font=pil_font, fill=lbl.color)
        
        base.save(save_path)
        messagebox.showinfo(APP_TITLE, f"已保存: {save_path}")

    def save_project(self):
        """保存工程文件"""
        if not self.app.image:
            messagebox.showinfo(APP_TITLE, "请先打开图片")
            return
            
        # 生成默认文件名
        try:
            default_name = self._generate_default_filename("json")
        except:
            default_name = "已标注.json"
            
        try:
            save_path = filedialog.asksaveasfilename(
                defaultextension=".json", 
                filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
                initialfile=default_name,
                title="保存工程文件"
            )
        except Exception as e:
            print(f"保存对话框错误: {e}")
            save_path = filedialog.asksaveasfilename(
                defaultextension=".json", 
                filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
            )
        if not save_path:
            return
            
        data = {
            "image_path": os.path.basename(self.app.image_path),
            "labels": [lbl.to_dict() for lbl in self.app.labels],
            "lines": [ln.to_dict() for ln in self.app.lines],
            "masks": [mask.to_dict() for mask in self.app.masks],
        }
        
        with open(save_path, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        messagebox.showinfo(APP_TITLE, f"工程已保存: {save_path}")

    def load_project(self):
        """加载工程文件"""
        path = filedialog.askopenfilename(filetypes=[("JSON", "*.json")])
        if not path:
            return
            
        with open(path, "r", encoding="utf-8") as f:
            data = json.load(f)
        
        img_path = data.get("image_path")
        # 如果保存的是相对路径，则从工程文件所在目录找
        if not os.path.isabs(img_path):
            img_path = os.path.join(os.path.dirname(path), img_path)
            
        if not os.path.exists(img_path):
            messagebox.showerror(APP_TITLE, f"图片文件不存在: {img_path}")
            return
            
        self.app.image_path = img_path
        self.app.image = Image.open(img_path).convert("RGBA")
        
        # 导入模型类
        from models import LabelItem, LineItem, MaskItem
        self.app.labels = [LabelItem.from_dict(lbl) for lbl in data.get("labels", [])]
        self.app.lines = [LineItem.from_dict(ln) for ln in data.get("lines", [])]
        self.app.masks = [MaskItem.from_dict(mask) for mask in data.get("masks", [])]
        
        self.app.fit_to_window()
        self.app.refresh_label_list()
        self.app.refresh_line_list()
        self.app.refresh_mask_list()
        messagebox.showinfo(APP_TITLE, "工程已加载")

    def _generate_default_filename(self, extension):
        """生成默认文件名"""
        if not self.app.image_path:
            return f"已标注.{extension}"
            
        # 获取原图片文件名（不含扩展名）
        base_name = os.path.splitext(os.path.basename(self.app.image_path))[0]
        
        # 生成新文件名
        return f"{base_name}_已标注.{extension}"

    def _get_mask_export_color(self, color, opacity):
        """获取遮罩导出颜色（真实透明度）"""
        try:
            if color.startswith('#'):
                r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)
                # 根据透明度与白色背景混合
                r = int(r * opacity + 255 * (1 - opacity))
                g = int(g * opacity + 255 * (1 - opacity))
                b = int(b * opacity + 255 * (1 - opacity))
                return f"#{r:02x}{g:02x}{b:02x}"
        except:
            pass
        return color

    def _draw_high_quality_line(self, draw, x1, y1, x2, y2, color, width):
        """绘制高质量抗锯齿线条用于导出"""
        import math
        
        # 使用PIL的基础线条绘制，但在高分辨率下效果更好
        width = max(1, int(width))
        draw.line([(x1, y1), (x2, y2)], fill=color, width=width)
        
        # 对于较粗的线条，添加额外的抗锯齿处理
        if width > 4:  # 提高阈值，减少不必要的处理
            # 计算线条方向
            dx = x2 - x1
            dy = y2 - y1
            length = math.sqrt(dx*dx + dy*dy)
            
            if length > 0:
                # 标准化方向向量
                ux = dx / length
                uy = dy / length
                
                # 垂直向量
                vx = -uy * 0.3  # 减小偏移量
                vy = ux * 0.3
                
                # 绘制额外的抗锯齿线条，但保持原色
                edge_width = max(1, width - 3)  # 更细的边缘线
                
                draw.line([(x1 + vx, y1 + vy), (x2 + vx, y2 + vy)], 
                         fill=color, width=edge_width)  # 使用原色
                draw.line([(x1 - vx, y1 - vy), (x2 - vx, y2 - vy)], 
                         fill=color, width=edge_width)  # 使用原色

